package cn.zhentao.service;

import io.minio.*;
import io.minio.http.Method;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.UUID;

/**
 * MinIO文件存储服务类
 * 提供文件上传、下载、删除等功能
 * 
 * @author 田国刚
 * @since 2023-04-16
 */
@Slf4j
@Service
public class MinioService {

    @Autowired
    private MinioClient minioClient;

    /**
     * 从配置文件中获取默认存储桶名称
     */
    @Value("${minio.bucket-name}")
    private String bucketName;

    /**
     * 检查存储桶是否存在，如果不存在则创建
     * 
     * @param bucketName 存储桶名称
     * @throws Exception 当操作失败时抛出异常
     */
    private void ensureBucketExists(String bucketName) throws Exception {
        // 检查存储桶是否存在
        boolean bucketExists = minioClient.bucketExists(
                BucketExistsArgs.builder()
                        .bucket(bucketName)
                        .build()
        );
        
        // 如果存储桶不存在，则创建它
        if (!bucketExists) {
            minioClient.makeBucket(
                    MakeBucketArgs.builder()
                            .bucket(bucketName)
                            .build()
            );
            log.info("创建存储桶成功: {}", bucketName);
        }
    }

    /**
     * 上传文件到MinIO
     * 
     * @param file 要上传的文件
     * @param fileName 文件名（如果为null，则自动生成UUID文件名）
     * @return 上传成功后的文件访问路径
     * @throws Exception 当上传失败时抛出异常
     */
    public String uploadFile(MultipartFile file, String fileName) throws Exception {
        // 确保存储桶存在
        ensureBucketExists(bucketName);
        
        // 如果没有指定文件名，则生成UUID文件名
        if (fileName == null || fileName.trim().isEmpty()) {
            String originalFilename = file.getOriginalFilename();
            String extension = "";
            if (originalFilename != null && originalFilename.contains(".")) {
                extension = originalFilename.substring(originalFilename.lastIndexOf("."));
            }
            fileName = UUID.randomUUID().toString() + extension;
        }
        
        // 上传文件到MinIO
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .stream(file.getInputStream(), file.getSize(), -1)
                        .contentType(file.getContentType())
                        .build()
        );
        
        log.info("文件上传成功: {}/{}", bucketName, fileName);
        return fileName;
    }

    /**
     * 上传字节数组到MinIO
     *
     * @param data 字节数组数据
     * @param fileName 文件名
     * @param contentType 文件类型
     * @return 上传成功后的文件访问路径
     * @throws Exception 当上传失败时抛出异常
     */
    public String uploadFile(byte[] data, String fileName, String contentType) throws Exception {
        return uploadFileToSpecificBucket(data, fileName, contentType, bucketName);
    }

    /**
     * 上传字节数组到指定的MinIO桶
     *
     * @param data 字节数组数据
     * @param fileName 文件名
     * @param contentType 文件类型
     * @param targetBucketName 目标桶名称
     * @return 上传成功后的文件访问路径
     * @throws Exception 当上传失败时抛出异常
     */
    public String uploadFileToSpecificBucket(byte[] data, String fileName, String contentType, String targetBucketName) throws Exception {
        // 确保存储桶存在
        ensureBucketExists(targetBucketName);

        // 如果没有指定文件名，则生成UUID文件名
        if (fileName == null || fileName.trim().isEmpty()) {
            fileName = UUID.randomUUID().toString() + ".mp3";
        }

        // 使用字节数组创建输入流
        try (InputStream inputStream = new ByteArrayInputStream(data)) {
            // 上传文件到MinIO
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(targetBucketName)
                            .object(fileName)
                            .stream(inputStream, data.length, -1)
                            .contentType(contentType != null ? contentType : "application/octet-stream")
                            .build()
            );
        }

        log.info("字节数组文件上传成功: {}/{}", targetBucketName, fileName);
        return fileName;
    }

    /**
     * 从MinIO下载文件
     *
     * @param fileName 文件名
     * @return 文件输入流
     * @throws Exception 当下载失败时抛出异常
     */
    public InputStream downloadFile(String fileName) throws Exception {
        return downloadFileFromSpecificBucket(fileName, bucketName);
    }

    /**
     * 从指定桶下载MinIO文件
     *
     * @param fileName 文件名
     * @param targetBucketName 目标桶名称
     * @return 文件输入流
     * @throws Exception 当下载失败时抛出异常
     */
    public InputStream downloadFileFromSpecificBucket(String fileName, String targetBucketName) throws Exception {
        return minioClient.getObject(
                GetObjectArgs.builder()
                        .bucket(targetBucketName)
                        .object(fileName)
                        .build()
        );
    }

    /**
     * 删除MinIO中的文件
     *
     * @param fileName 要删除的文件名
     * @throws Exception 当删除失败时抛出异常
     */
    public void deleteFile(String fileName) throws Exception {
        deleteFileFromSpecificBucket(fileName, bucketName);
    }

    /**
     * 从指定桶删除MinIO中的文件
     *
     * @param fileName 要删除的文件名
     * @param targetBucketName 目标桶名称
     * @throws Exception 当删除失败时抛出异常
     */
    public void deleteFileFromSpecificBucket(String fileName, String targetBucketName) throws Exception {
        minioClient.removeObject(
                RemoveObjectArgs.builder()
                        .bucket(targetBucketName)
                        .object(fileName)
                        .build()
        );
        log.info("文件删除成功: {}/{}", targetBucketName, fileName);
    }

    /**
     * 检查文件是否存在
     *
     * @param fileName 文件名
     * @return 如果文件存在返回true，否则返回false
     */
    public boolean fileExists(String fileName) {
        return fileExistsInSpecificBucket(fileName, bucketName);
    }

    /**
     * 检查指定桶中的文件是否存在
     *
     * @param fileName 文件名
     * @param targetBucketName 目标桶名称
     * @return 如果文件存在返回true，否则返回false
     */
    public boolean fileExistsInSpecificBucket(String fileName, String targetBucketName) {
        try {
            minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(targetBucketName)
                            .object(fileName)
                            .build()
            );
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取文件的预签名URL（用于直接访问）
     * 
     * @param fileName 文件名
     * @param expiry 过期时间（秒）
     * @return 预签名URL
     * @throws Exception 当获取URL失败时抛出异常
     */
    public String getPresignedUrl(String fileName, int expiry) throws Exception {
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(bucketName)
                        .object(fileName)
                        .expiry(expiry)
                        .build()
        );
    }
}
